﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;

namespace Songhay.Composition
{
    using Songhay.Extensions;

    /// <summary>
    /// Defines an entry-point for MEF Composition.
    /// </summary>
    public abstract class CompositionHost : IDisposable
    {
        /// <summary>
        /// Initializes a new instance
        /// of the <see cref="CompositionHost"/> class.
        /// </summary>
        public CompositionHost()
        {
            this.AggregateCatalog = new AggregateCatalog();
            this.CompositionBatch = new CompositionBatch();
            this.CompositionBatch.AddPart(this);
            this.CompositionContainer =
                new CompositionContainer(this.AggregateCatalog);
        }

        /// <summary>
        /// Gets or sets the composition messaging
        /// of the application domain.
        /// </summary>
        /// <value>The composition messaging.</value>
        [ImportMany(typeof(ICompositionMessaging))]
        public IEnumerable<ICompositionMessaging> CompositionMessaging { get; set; }

#if !SILVERLIGHT

        /// <summary>
        /// Adds to MEF aggregate catalog
        /// with catalog initialization.
        /// </summary>
        /// <param name="pathToDirectoryOrAssembly">The path to the directory or assembly.</param>
        public CompositionHost AddToAggregateCatalog(string pathToDirectoryOrAssembly)
        {
            return this.AddToAggregateCatalog(
                    CompositionUtility
                        .GetDirectoryOrAssemblyCatalog(pathToDirectoryOrAssembly));
        }

#endif

        /// <summary>
        /// Adds to MEF aggregate catalog
        /// with catalog initialization.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        public CompositionHost AddToAggregateCatalog(ComposablePartCatalog catalog)
        {
            this.AggregateCatalog.Catalogs.Add(catalog);
            return this;
        }

        /// <summary>
        /// Adds to MEF composition batch of parts.
        /// </summary>
        /// <param name="part">The part.</param>
        public CompositionHost AddToCompositionBatch(object part)
        {
            this.CompositionBatch.AddPart(part);
            return this;
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated
        /// with freeing, releasing,
        /// or resetting unmanaged resources.
        /// </summary>
        /// <remarks>
        /// Do not make this method virtual.
        /// A derived class should not be able to override this method.
        /// [http://msdn.microsoft.com/en-us/library/system.idisposable.aspx]
        /// </remarks>
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Gets or sets the MEF aggregate catalog of exports.
        /// </summary>
        /// <value>The aggregate catalog.</value>
        protected AggregateCatalog AggregateCatalog { get; set; }

        /// <summary>
        /// Gets or sets the MEF composition batch of parts.
        /// </summary>
        /// <value>The composition batch.</value>
        protected CompositionBatch CompositionBatch { get; set; }

        /// <summary>
        /// Gets or sets the MEF composition container.
        /// </summary>
        /// <value>The composition container.</value>
        protected CompositionContainer CompositionContainer { get; set; }

        /// <summary>
        /// Composes the MEF aggregate catalog of exports.
        /// </summary>
        protected void Compose()
        {
            this.CompositionContainer.Compose(this.CompositionBatch);
        }

        /// <summary>
        /// Composes the specified MEF parts
        /// and the aggregate catalog of exports.
        /// </summary>
        /// <param name="compositionSet">The composition set.</param>
        protected void Compose(params object[] compositionSet)
        {
            if(compositionSet != null)
                compositionSet.ForEachInEnumerable(o => this.CompositionBatch.AddPart(o));
            this.Compose();
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="CompositionHost"/> is reclaimed by garbage collection.
        /// </summary>
        ~CompositionHost()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing">
        ///     <c>true</c> to release both managed and unmanaged resources;
        ///     <c>false</c> to release only unmanaged resources.
        /// </param>
        /// <remarks>
        /// Dispose(bool disposing) executes in two distinct scenarios.
        /// If disposing equals true, the method has been called directly
        /// or indirectly by a user's code. Managed and unmanaged resources
        /// can be disposed.
        /// If disposing equals false, the method has been called by the
        /// runtime from inside the finalizer and you should not reference
        /// other objects. Only unmanaged resources can be disposed.
        /// </remarks>
        void Dispose(bool disposing)
        {
            if(!this._disposed)
            {
                if(disposing)
                {
                    if(this.CompositionContainer != null)
                        this.CompositionContainer.Dispose();

                    if(this.AggregateCatalog != null)
                        this.AggregateCatalog.Dispose();
                }

                this._disposed = true;

            }
        }

        bool _disposed;
    }
}
